#include <arch/types.h>
#include <arch/CPUInfo.h>

char CPUInfo::Vendor[12];
char CPUInfo::Name[48];
struct CPUType CPUInfo::Type;
struct CPUBrand CPUInfo::Brand;
struct CPUFeatures0 CPUInfo::Features0;
struct CPUFeatures1 CPUInfo::Features1;

int CPUInfo::maxLevel, CPUInfo::maxExtendedLevel;

void CPUInfo::CPUID(dword *eax, dword *ebx, dword *ecx, dword *edx) {
	asm ("CPUID"
		:"=a" (*eax), "=b" (*ebx), "=c" (*ecx), "=d" (*edx)
		:"a" (*eax));
}

int CPUInfo::GetVendor() {
	dword eax = 0, ebx, ecx, edx;

	CPUID(&eax, &ebx, &ecx, &edx);

	*((dword *) &Vendor[0]) = ebx;
	*((dword *) &Vendor[4]) = edx;
	*((dword *) &Vendor[8]) = ecx;

	return eax;
	
}

void CPUInfo::GetProcessorName() {
	dword eax = 0x80000002, ebx, ecx, edx;

	CPUID(&eax, &ebx, &ecx, &edx);

	*((dword *) &Name[0]) = eax;
	*((dword *) &Name[4]) = ebx;
	*((dword *) &Name[8]) = ecx;
	*((dword *) &Name[12]) = edx;

	eax = 0x80000003;

	CPUID(&eax, &ebx, &ecx, &edx);

	*((dword *) &Name[16]) = eax;
	*((dword *) &Name[20]) = ebx;
	*((dword *) &Name[24]) = ecx;
	*((dword *) &Name[28]) = edx;

	eax = 0x80000004;

	CPUID(&eax, &ebx, &ecx, &edx);

	*((dword *) &Name[32]) = eax;
	*((dword *) &Name[36]) = ebx;
	*((dword *) &Name[40]) = ecx;
	*((dword *) &Name[44]) = edx;
}

void CPUInfo::GetFeatures() {
	CPUID((dword *) &Type, (dword *) &Brand, (dword *) &Features1, (dword *) &Features0);
}

void CPUInfo::GetExtendedFeatures() {
	/* In some way we need (dword) ExtendedType == 0x80000001 */

//	CPUID((dword *) &ExtendedType, (dword *) &ExtendedBrand, (dword *) &Features3, (dword *) &Features2);
}

int CPUInfo::GetMaxExtendedLevel() {
	dword eax = 0x80000000, ebx, ecx, edx;

	CPUID(&eax, &ebx, &ecx, &edx);

	/* ebx, edx, and ecx contains the same as in GetFeatures, so no need to
	 * use this again
	 */

	return eax;
}

int CPUInfo::GetInformations() {
	maxLevel = GetVendor();

	if (maxLevel > 0)
		GetFeatures();

	if (maxLevel > 1)
		;	/* Don't know how to use the information in CPUID(2) so, don't waste time */

	if (maxLevel > 2 && Features0.PSN)
		;	/* Why should we read the serial number? */

	if (maxLevel > 3)
		;	/* Cache informations */

	if (maxLevel > 4)
		;	/* MONITOR/MWAIT informations */

	if (maxLevel > 5)
		;	/* Power management informations... someone should work with this */

	/*
	 * Well, there are others, but i don't think ANY processor supports
	 * them. However if someone wish to implements them, you are incouraged
	 * to do so
	 */

	maxExtendedLevel = GetMaxExtendedLevel();

	if (maxExtendedLevel > 0)
		GetExtendedFeatures();

	if (maxExtendedLevel >= 4)
		GetProcessorName();

	/*
	 * OK, at this point I think we got all the most useful and intersing
	 * values. If you need others, just do it. Only remember to check that
	 * CPUID value exists on the current processor using "if (.... > ...)"
	 */

	return maxLevel;
}
